#pragma once
#include <vector>
#include <random>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <string>
#include <memory>
#include <mutex>
#include <atomic>
#include "../common/util.hpp"
#include "../common/asynclog.hpp"

namespace yx
{
    class Machine
    {
    private:
        std::string _ip;                           // 编译运行主机的ip地址
        int _port = 0;                             // 编译运行主机的端口
        std::shared_ptr<std::atomic_int> _load; // 编译运行主机的负载
        bool _valid = false;

    public:
        Machine(const std::string &ip, int port, int load = 0)
            : _ip(ip), _port(port), _load(std::make_shared<std::atomic_int>(load)), _valid(true)
        {
        }

        Machine() = default;

        Machine(const Machine &m)
            : _ip(m._ip), _port(m._port), _load(m._load), _valid(m._valid)
        {
        }

        Machine(const Machine &&m)
            : _ip(std::move(m._ip)), _port(m._port), _load(std::move(m._load)), _valid(m._valid)
        {
        }

        Machine &operator=(const Machine &m)
        {
            _ip = m._ip;
            _port = m._port;
            _load = m._load;
            _valid = m._valid;
            return *this;
        }

        Machine &operator=(const Machine &&m)
        {
            _ip = std::move(m._ip);
            _port = m._port;
            _load = std::move(m._load);
            _valid = m._valid;
            return *this;
        }

        ~Machine()
        {
        }

        // 只能使用前置++及前置--
        Machine &operator++()
        {
            (*_load)++;

            return *this;
        }

        Machine &operator--()
        {
            (*_load)--;

            return *this;
        }

        int getLoad()
        {
            return _load->load();
        }

        void setLoad(int num)
        {
            _load->store(num);
        }

        void resetLoad()
        {
            _load->store(0);
        }

        bool isValid()
        {
            return _valid;
        }

        const std::string &getIp()
        {
            return _ip;
        }

        const int &getPort()
        {
            return _port;
        }
    };

    static const std::string machines_conf_path = "./confdir/machines.conf";

    class LoadBalancer
    {
    private:
        // machine在vector<Machine>中的下标表示对应的id
        std::vector<Machine> _machines;
        // 记录在线主机的id
        std::vector<int> _online;
        // 记录离线主机的id
        std::vector<int> _offline;
        // 互斥锁
        std::mutex _mtx;

    public:
        LoadBalancer()
        {
            assert(loadMachines(machines_conf_path));
        }

        bool loadMachines(const std::string &machines_conf)
        {
            std::ifstream fin(machines_conf);
            if (!fin.is_open())
            {
                (FLOG()("打开编译运行主机配置文件失败!!!"));
                return false;
            }

            std::string line;
            while (std::getline(fin, line))
            {
                std::vector<std::string> tokens;
                if (StringUtil::cutString(line, ":", tokens) != 2)
                {
                    (WLOG()("加载某个编译运行主机信息失败!!!"));
                    continue;
                }

                Machine m(tokens[0], atoi(tokens[1].c_str()));

                // 记录上线主机的id
                _online.push_back(_machines.size());
                _machines.push_back(std::move(m));
            }

            (ILOG()("加载编译运行主机信息成功,配置文件 {} !!!", machines_conf_path));
            fin.close();
            return true;
        }

        bool getMinLoadMachine(int &id, Machine &m)
        {
            std::unique_lock<std::mutex> ul(_mtx);

            if (_online.empty())
            {
                (FLOG()("所有后端编译运行主机已经离线!!!"));
                return false;
            }

            id = _online.front();
            m = _machines[_online.front()];
            int minload = _machines[_online.front()].getLoad();

            for (auto e : _online)
            {
                if (minload > _machines[e].getLoad())
                {
                    id = e;
                    m = _machines[e];
                    minload = _machines[e].getLoad();
                }
            }

            return true;
        }

        void offlineMachine(int which)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            for (auto it = _online.begin(); it != _online.end(); ++it)
            {
                if (*it == which)
                {
                    // 重置负载数
                    _machines[*it].resetLoad();
                    _offline.push_back(*it);
                    _online.erase(it);
                    break;
                }
            }
        }

        void onlineMachines()
        {
            {
                std::unique_lock<std::mutex> ul(_mtx);

                for (auto e : _offline)
                {
                    _online.push_back(e);
                }
                _offline.clear();
            }
            (ILOG()("上线编译运行主机成功_online_size: {} !!!",(int)_online.size()));
        }

        void showMachines()
        {
            std::unique_lock<std::mutex> ul(_mtx);
            std::cout << "当前在线主机列表: ";
            for (auto &id : _online)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            std::cout << "当前离线主机列表: ";
            for (auto &id : _offline)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
        }

        ~LoadBalancer()
        {
        }
    };

    namespace load_balance_algorithm
    {
        class LoadBalance
        {
        protected:
            // machine在vector<Machine>中的下标表示对应的id
            std::vector<Machine> _machines;
            // 记录在线主机的id
            std::vector<int> _online;
            // 记录离线主机的id
            std::vector<int> _offline;
            // 互斥锁
            std::mutex _mtx;

        public:
            LoadBalance() {}
            virtual ~LoadBalance() {}

            void addMachine(const std::string &ip, int port)
            {
                _online.push_back(_machines.size());
                _machines.push_back(Machine(ip, port));
            }

            virtual bool optimalChoice(int &id, Machine &m) = 0;
        };

        // 轮询
        class RoundRobin : public LoadBalance
        {
        private:
            int _curr_index = 0; // _online的下标

        public:
            virtual bool optimalChoice(int &id, Machine &m) override
            {
                std::unique_lock<std::mutex> ul(_mtx);

                if (_online.empty())
                {
                    (FLOG()("所有后端编译运行主机已经离线!!!"));
                    return false;
                }

                // 选择在线主机
                id = _online[_curr_index];
                m = _machines[_online[_curr_index]];

                _curr_index = (_curr_index + 1) % _online.size();

                return true;
            }
        };

        // 随机
        class Random : public LoadBalance
        {
        private:
        public:
            virtual bool optimalChoice(int &id, Machine &m) override
            {
                std::unique_lock<std::mutex> ul(_mtx);

                if (_online.empty())
                {
                    (FLOG()("所有后端编译运行主机已经离线!!!"));
                    return false;
                }

                std::random_device rd;
                std::mt19937 gen(rd());
                std::uniform_int_distribution<int> dis(0, _machines.size() - 1);

                while (true)
                {
                    // 随机选择主机并且是在线的
                    int index = dis(gen);
                    auto pos = std::find(_online.begin(), _online.end(), index);
                    if (pos != _online.end())
                    {
                        id = _online[index];
                        m = _machines[_online[index]];
                        break;
                    }
                }

                return true;
            }
        };
    }
}
